Repository navigation
feat(desktop): running agents section in workspace sidebar - #5414
Conversation
Show a per-workspace row of currently running agents (working / awaiting permission) beneath the workspace item, each badge linking to the agent. Decoupled from the experimental inline-ports flag: the details container renders whenever it has content, the ports section stays gated by inlineWorkspacePortsEnabled, and the agents section shows independently.
Drive the workspace agents row from terminal-agent bindings so every bound agent shows (not only working/permission), resolving name + preset icon from agentId. The leading glyph is the live state indicator when active, and the agent icon when idle, in a fixed-size slot so the row doesn't shift. Both the agents and ports rows now wrap (single row, second only when needed) and the agents row is decoupled from the experimental inline-ports flag.
|
Ready to review this PR? Stage has broken it down into 5 individual chapters for you: Chapters generated by Stage for commit 6cb32c6 on Jul 2, 2026 4:59pm UTC. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds a workspace-agents experiment setting and persisted enablement state, then wires a running-agents sidebar section into dashboard workspace details with clickable agent badges and updated ports rendering. ChangesWorkspace agents setting
Dashboard sidebar agents
Workspace details and ports
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryAdds a running agents row to each workspace entry in the v2 dashboard sidebar. Agents bound to a workspace's terminals are fetched via
Confidence Score: 4/5Safe to merge; the new agents row is purely additive, the flag-decoupling is handled correctly inside the details component, and no existing data paths are broken. The feature works correctly end-to-end: bindings are fetched, statuses are resolved with a proper idle fallback, keys are stable, and click navigation is wired correctly. The one design concern is that DashboardSidebarWorkspaceDetails.tsx — the double hook invocation for agents is worth addressing in a follow-up; all other files are straightforward.
|
| Filename | Overview |
|---|---|
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/DashboardSidebarWorkspaceDetails.tsx | Adds agents section to the details panel; calls useDashboardSidebarWorkspaceRunningAgents for counting but the rendered child (AgentsRow) independently calls the same hook, duplicating memo computation. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/hooks/useDashboardSidebarWorkspaceRunningAgents/useDashboardSidebarWorkspaceRunningAgents.ts | New hook that builds a sorted list of agents from terminal bindings + notification store status; logic is correct with proper idle fallback and stable sort. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/DashboardSidebarWorkspaceAgentBadge.tsx | Badge renders the correct icon/status indicator per agent state; status narrowing before StatusIndicator call is type-safe; tooltip content is well-structured. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsx | Removes the inlineWorkspacePortsEnabled gate from the parent so DashboardSidebarWorkspaceDetails always mounts; the gate is now correctly handled inside the details component. |
| apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspacePortsRow/DashboardSidebarWorkspacePortsRow.tsx | Replaces OverflowFadeContainer+grid with a flex-wrap div; intentional UX change removing horizontal scroll in favor of line wrapping. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Item as "WorkspaceItem"
participant Det as "WorkspaceDetails"
participant Row as "AgentsRow"
participant Hook as "useRunningAgents"
participant Query as "useTerminalAgentBindings"
participant ZStore as "useV2NotificationStore"
Item->>Det: "render (always when not pending)"
Det->>Hook: "call #1 — for agentCount"
Hook->>Query: "fetch bindings (cached)"
Hook->>ZStore: "subscribe to terminal statuses"
Hook-->>Det: "DashboardSidebarRunningAgent[]"
Det->>Row: "render content (agentCount > 0)"
Row->>Hook: "call #2 — for rendering"
Hook->>Query: "same key → cache hit"
Hook->>ZStore: "second subscription"
Hook-->>Row: "DashboardSidebarRunningAgent[]"
Row->>Row: "render AgentBadge per agent"
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Item as "WorkspaceItem"
participant Det as "WorkspaceDetails"
participant Row as "AgentsRow"
participant Hook as "useRunningAgents"
participant Query as "useTerminalAgentBindings"
participant ZStore as "useV2NotificationStore"
Item->>Det: "render (always when not pending)"
Det->>Hook: "call #1 — for agentCount"
Hook->>Query: "fetch bindings (cached)"
Hook->>ZStore: "subscribe to terminal statuses"
Hook-->>Det: "DashboardSidebarRunningAgent[]"
Det->>Row: "render content (agentCount > 0)"
Row->>Hook: "call #2 — for rendering"
Hook->>Query: "same key → cache hit"
Hook->>ZStore: "second subscription"
Hook-->>Row: "DashboardSidebarRunningAgent[]"
Row->>Row: "render AgentBadge per agent"
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/DashboardSidebarWorkspaceDetails.tsx:79-92
**Duplicate hook invocation per workspace render**
`useDashboardSidebarWorkspaceRunningAgents` is called here to get `agentCount` and then called again inside `DashboardSidebarWorkspaceAgentsRow` (which this code renders as `content`). Unlike `useDashboardSidebarWorkspacePorts`, which reads a lightweight context, this hook runs a `useMemo` with a sort on every call, plus two separate `useV2NotificationStore` subscriptions. For workspaces with active agents both subscriptions fire on every status change, doubling the memo work.
Consider accepting the pre-computed list as a prop in `DashboardSidebarWorkspaceAgentsRow`, or lifting the hook call to a shared context the way ports use `DashboardSidebarPortsProvider`.
Reviews (1): Last reviewed commit: "feat(desktop): show all bound agents wit..." | Re-trigger Greptile
| const runningAgents = useDashboardSidebarWorkspaceRunningAgents(workspaceId); | ||
| const agentCount = runningAgents.length; | ||
| if (agentCount > 0) { | ||
| sections.push({ | ||
| key: "agents", | ||
| summary: `${agentCount} ${agentCount === 1 ? "agent" : "agents"}`, | ||
| content: ( | ||
| <DashboardSidebarWorkspaceAgentsRow | ||
| workspaceId={workspaceId} | ||
| isInSection={isInSection} | ||
| /> | ||
| ), | ||
| }); | ||
| } |
There was a problem hiding this comment.
Duplicate hook invocation per workspace render
useDashboardSidebarWorkspaceRunningAgents is called here to get agentCount and then called again inside DashboardSidebarWorkspaceAgentsRow (which this code renders as content). Unlike useDashboardSidebarWorkspacePorts, which reads a lightweight context, this hook runs a useMemo with a sort on every call, plus two separate useV2NotificationStore subscriptions. For workspaces with active agents both subscriptions fire on every status change, doubling the memo work.
Consider accepting the pre-computed list as a prop in DashboardSidebarWorkspaceAgentsRow, or lifting the hook call to a shared context the way ports use DashboardSidebarPortsProvider.
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/DashboardSidebarWorkspaceDetails.tsx
Line: 79-92
Comment:
**Duplicate hook invocation per workspace render**
`useDashboardSidebarWorkspaceRunningAgents` is called here to get `agentCount` and then called again inside `DashboardSidebarWorkspaceAgentsRow` (which this code renders as `content`). Unlike `useDashboardSidebarWorkspacePorts`, which reads a lightweight context, this hook runs a `useMemo` with a sort on every call, plus two separate `useV2NotificationStore` subscriptions. For workspaces with active agents both subscriptions fire on every status change, doubling the memo work.
Consider accepting the pre-computed list as a prop in `DashboardSidebarWorkspaceAgentsRow`, or lifting the hook call to a shared context the way ports use `DashboardSidebarPortsProvider`.
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/DashboardSidebarWorkspaceAgentBadge.tsx (1)
12-12: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valuePrefer path alias over relative import.
Other imports in this file use
renderer/...aliases; this one uses a relative path instead.♻️ Suggested alias import
-import type { DashboardSidebarRunningAgent } from "../../hooks/useDashboardSidebarWorkspaceRunningAgents"; +import type { DashboardSidebarRunningAgent } from "renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/hooks/useDashboardSidebarWorkspaceRunningAgents";As per coding guidelines: "When possible, use the path aliases defined in
tsconfig.jsoninstead of relative imports."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/DashboardSidebarWorkspaceAgentBadge.tsx` at line 12, The import in DashboardSidebarAgentBadge should use the existing renderer path alias instead of a relative path to match the rest of the file. Update the DashboardSidebarWorkspaceAgentBadge component’s DashboardSidebarRunningAgent import so it follows the same alias-based pattern used by the other imports in this module.Source: Coding guidelines
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/DashboardSidebarWorkspaceDetails.tsx (1)
8-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winImport the hook via the sibling barrel instead of its internal path.
useDashboardSidebarWorkspaceRunningAgentsis imported by reaching directly intoDashboardSidebarWorkspaceAgentsRow/hooks/..., but the sibling barrel (DashboardSidebarWorkspaceAgentsRow/index.ts) already re-exports both the component and the hook. This bypasses the component's public interface and couples this file to its internal folder layout.🔧 Proposed fix
-import { DashboardSidebarWorkspaceAgentsRow } from "../DashboardSidebarWorkspaceAgentsRow"; -import { useDashboardSidebarWorkspaceRunningAgents } from "../DashboardSidebarWorkspaceAgentsRow/hooks/useDashboardSidebarWorkspaceRunningAgents"; +import { + DashboardSidebarWorkspaceAgentsRow, + useDashboardSidebarWorkspaceRunningAgents, +} from "../DashboardSidebarWorkspaceAgentsRow";As per coding guidelines,
**/components/**/*.tsx: "Use one folder per component with structureComponentName/ComponentName.tsx+index.tsfor barrel export".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/DashboardSidebarWorkspaceDetails.tsx` around lines 8 - 9, The import in DashboardSidebarWorkspaceDetails should use the sibling DashboardSidebarWorkspaceAgentsRow barrel instead of the internal hooks path. Update the imports so both DashboardSidebarWorkspaceAgentsRow and useDashboardSidebarWorkspaceRunningAgents come from the public index export, keeping this component decoupled from the folder’s internal structure and aligned with the one-folder-per-component barrel pattern.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/DashboardSidebarWorkspaceAgentBadge.tsx`:
- Around line 50-60: The non-idle badge state is rendering smaller than the idle
icon inside the fixed DashboardSidebarWorkspaceAgentBadge slot. Update the
DashboardSidebarWorkspaceAgentBadge rendering so the StatusIndicator used for
agent.status !== "idle" matches the 14px container, either by passing a matching
className at the call site or by increasing StatusIndicator’s default size where
it is defined.
---
Nitpick comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/DashboardSidebarWorkspaceAgentBadge.tsx`:
- Line 12: The import in DashboardSidebarAgentBadge should use the existing
renderer path alias instead of a relative path to match the rest of the file.
Update the DashboardSidebarWorkspaceAgentBadge component’s
DashboardSidebarRunningAgent import so it follows the same alias-based pattern
used by the other imports in this module.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/DashboardSidebarWorkspaceDetails.tsx`:
- Around line 8-9: The import in DashboardSidebarWorkspaceDetails should use the
sibling DashboardSidebarWorkspaceAgentsRow barrel instead of the internal hooks
path. Update the imports so both DashboardSidebarWorkspaceAgentsRow and
useDashboardSidebarWorkspaceRunningAgents come from the public index export,
keeping this component decoupled from the folder’s internal structure and
aligned with the one-folder-per-component barrel pattern.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9d9db782-9477-4061-95e4-8b99f986b05d
📒 Files selected for processing (9)
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/DashboardSidebarWorkspaceItem.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/DashboardSidebarWorkspaceAgentsRow.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/DashboardSidebarWorkspaceAgentBadge.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/hooks/useDashboardSidebarWorkspaceRunningAgents/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/hooks/useDashboardSidebarWorkspaceRunningAgents/useDashboardSidebarWorkspaceRunningAgents.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/index.tsapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/DashboardSidebarWorkspaceDetails.tsxapps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspacePortsRow/DashboardSidebarWorkspacePortsRow.tsx
| <span className="flex size-3.5 shrink-0 items-center justify-center"> | ||
| {agent.status === "idle" ? ( | ||
| iconUrl ? ( | ||
| <img src={iconUrl} alt="" className="size-3.5 object-contain" /> | ||
| ) : ( | ||
| <LuBot className="size-3.5" strokeWidth={STROKE_WIDTH} /> | ||
| ) | ||
| ) : ( | ||
| <StatusIndicator status={agent.status} /> | ||
| )} | ||
| </span> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
fd -t f "StatusIndicator" apps/desktop/src/renderer/screens/main/components/StatusIndicator --exec cat -n {}Repository: superset-sh/superset
Length of output: 2336
Match StatusIndicator to the 14px badge slot. It currently renders at size-2 (8px), so the non-idle state reads smaller than the icon state inside the fixed size-3.5 container. Pass a matching className here or bump the default indicator size.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/DashboardSidebarWorkspaceAgentBadge.tsx`
around lines 50 - 60, The non-idle badge state is rendering smaller than the
idle icon inside the fixed DashboardSidebarWorkspaceAgentBadge slot. Update the
DashboardSidebarWorkspaceAgentBadge rendering so the StatusIndicator used for
agent.status !== "idle" matches the 14px container, either by passing a matching
className at the call site or by increasing StatusIndicator’s default size where
it is defined.
There was a problem hiding this comment.
2 issues found across 9 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/DashboardSidebarWorkspaceDetails.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/DashboardSidebarWorkspaceDetails.tsx:79">
P2: This hook is invoked here to compute `agentCount`, but `DashboardSidebarWorkspaceAgentsRow` (rendered as `content` below) calls the same hook internally. Unlike `useDashboardSidebarWorkspacePorts` which reads from a shared context provider, `useDashboardSidebarWorkspaceRunningAgents` sets up its own `useV2NotificationStore` subscription and runs a `useMemo` sort each time. This doubles the subscription and memo work for every workspace with agents.
Consider either passing the already-computed `runningAgents` list as a prop to `DashboardSidebarWorkspaceAgentsRow`, or lifting the hook into a shared context/provider pattern similar to how ports use `DashboardSidebarPortsProvider`.</violation>
</file>
<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/DashboardSidebarWorkspaceAgentBadge.tsx">
<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/DashboardSidebarWorkspaceAgentBadge.tsx:58">
P3: The `StatusIndicator` renders at its default `size-2` (8px) inside the fixed `size-3.5` (14px) container. In contrast, the idle state renders icons at the full `size-3.5`. This size mismatch means the non-idle state glyph appears noticeably smaller than the idle icon, which undermines the goal of a fixed-size slot with no layout shifts. Consider passing a `className` to scale the indicator to match the 14px slot (e.g., `className="size-3.5"`) or wrapping it to fill the container.</violation>
</file>
Architecture diagram
sequenceDiagram
participant DSWI as DashboardSidebarWorkspaceItem
participant DSWD as DashboardSidebarWorkspaceDetails
participant DSWAR as DashboardSidebarWorkspaceAgentsRow
participant Hook as useDashboardSidebarWorkspaceRunningAgents
participant Bindings as useTerminalAgentBindings
participant Notif as useV2NotificationStore
participant Badge as DashboardSidebarWorkspaceAgentBadge
participant Router as react-router navigate
DSWI->>DSWD: render details
Note over DSWD: Decoupled from inlineWorkspacePortsEnabled flag
DSWD->>Hook: useDashboardSidebarWorkspaceRunningAgents(workspaceId)
Hook->>Bindings: get terminal agent bindings for workspace
Bindings-->>Hook: Map<terminalId, {agentId, startedAt, …}>
Hook->>Notif: subscribe to notification statuses
Notif-->>Hook: statusByTerminal map (working/permission/review/idle)
Note over Hook: Resolve agentId -> label via BUILTIN_AGENT_LABELS
Hook->>Hook: sort bindings by startedAt ascending
Hook-->>DSWD: DashboardSidebarRunningAgent[]
DSWD->>DSWD: check agentCount > 0
alt No agents
DSWD-->>DSWI: render nothing for agents section
else Has agents
DSWD->>DSWAR: render agents row with agents[]
DSWAR->>DSWAR: agents.length === 0? return null
DSWAR->>Badge: render badge per agent
Note over Badge: Fixed-size 14px icon slot to prevent layout shift
alt Agent status is "idle"
Badge->>Badge: render agent icon (preset icon or fallback LuBot)
else Agent is working/permission/review
Badge->>Badge: render StatusIndicator(status)
end
Badge->>Badge: render label (truncated)
Badge->>Router: onClick → navigateToV2Workspace with terminalId + focusRequestId
Router-->>Badge: terminal pane opens with agent focused
end
Note over DSWD: Ports row still gated by inlineWorkspacePortsEnabled
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| } | ||
|
|
||
| // Add more detail rows here (e.g. running agents). | ||
| const runningAgents = useDashboardSidebarWorkspaceRunningAgents(workspaceId); |
There was a problem hiding this comment.
P2: This hook is invoked here to compute agentCount, but DashboardSidebarWorkspaceAgentsRow (rendered as content below) calls the same hook internally. Unlike useDashboardSidebarWorkspacePorts which reads from a shared context provider, useDashboardSidebarWorkspaceRunningAgents sets up its own useV2NotificationStore subscription and runs a useMemo sort each time. This doubles the subscription and memo work for every workspace with agents.
Consider either passing the already-computed runningAgents list as a prop to DashboardSidebarWorkspaceAgentsRow, or lifting the hook into a shared context/provider pattern similar to how ports use DashboardSidebarPortsProvider.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceDetails/DashboardSidebarWorkspaceDetails.tsx, line 79:
<comment>This hook is invoked here to compute `agentCount`, but `DashboardSidebarWorkspaceAgentsRow` (rendered as `content` below) calls the same hook internally. Unlike `useDashboardSidebarWorkspacePorts` which reads from a shared context provider, `useDashboardSidebarWorkspaceRunningAgents` sets up its own `useV2NotificationStore` subscription and runs a `useMemo` sort each time. This doubles the subscription and memo work for every workspace with agents.
Consider either passing the already-computed `runningAgents` list as a prop to `DashboardSidebarWorkspaceAgentsRow`, or lifting the hook into a shared context/provider pattern similar to how ports use `DashboardSidebarPortsProvider`.</comment>
<file context>
@@ -72,7 +76,20 @@ export function DashboardSidebarWorkspaceDetails({
}
- // Add more detail rows here (e.g. running agents).
+ const runningAgents = useDashboardSidebarWorkspaceRunningAgents(workspaceId);
+ const agentCount = runningAgents.length;
+ if (agentCount > 0) {
</file context>
| <LuBot className="size-3.5" strokeWidth={STROKE_WIDTH} /> | ||
| ) | ||
| ) : ( | ||
| <StatusIndicator status={agent.status} /> |
There was a problem hiding this comment.
P3: The StatusIndicator renders at its default size-2 (8px) inside the fixed size-3.5 (14px) container. In contrast, the idle state renders icons at the full size-3.5. This size mismatch means the non-idle state glyph appears noticeably smaller than the idle icon, which undermines the goal of a fixed-size slot with no layout shifts. Consider passing a className to scale the indicator to match the 14px slot (e.g., className="size-3.5") or wrapping it to fill the container.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/DashboardSidebar/components/DashboardSidebarWorkspaceItem/components/DashboardSidebarWorkspaceAgentsRow/components/DashboardSidebarWorkspaceAgentBadge/DashboardSidebarWorkspaceAgentBadge.tsx, line 58:
<comment>The `StatusIndicator` renders at its default `size-2` (8px) inside the fixed `size-3.5` (14px) container. In contrast, the idle state renders icons at the full `size-3.5`. This size mismatch means the non-idle state glyph appears noticeably smaller than the idle icon, which undermines the goal of a fixed-size slot with no layout shifts. Consider passing a `className` to scale the indicator to match the 14px slot (e.g., `className="size-3.5"`) or wrapping it to fill the container.</comment>
<file context>
@@ -0,0 +1,75 @@
+ <LuBot className="size-3.5" strokeWidth={STROKE_WIDTH} />
+ )
+ ) : (
+ <StatusIndicator status={agent.status} />
+ )}
+ </span>
</file context>
Add a 'Workspace agents' experimental toggle (off by default) mirroring the inline-ports experiment: new store, settings-search entry, and Experimental settings switch. The agents section only renders when enabled, and the per-workspace terminal-agent-binding queries are skipped when disabled via a threaded `enabled` flag.
🧹 Preview Cleanup CompleteThe following preview resources have been cleaned up:
Thank you for your contribution! 🎉 |
What
Adds a running agents row beneath each workspace in the v2 dashboard sidebar, mirroring the inline ports row.
useTerminalAgentBindings), not just those currentlyworking/permission— idle agents show too.agentId(BUILTIN_AGENT_LABELS+usePresetIcon), the same resolver the terminal pane icon uses.StatusIndicatorwhen the agent isworking/permission/review, and the agent's icon whenidle— held in a fixed-size slot so the row doesn't shift as state changes. No trailing status element.terminalId+focusRequestId), same as the port badge.startedAtfor stable positioning; the details toggle summary counts all bound agents.Layout / decoupling
inlineWorkspacePortsEnabledflag — the details container renders whenever it has content; only the ports section stays gated by the flag (so ports don't double-render against the bottom ports list).flex flex-wrap— a single row that wraps to a second only when needed (previously a forced 2-row horizontal-scroll grid), with tighter spacing.Notes
Test plan
Summary by cubic
Adds a running agents row under each workspace in the v2 dashboard sidebar, showing all terminal-bound agents with live status and one-click focus to the agent’s terminal. The feature is behind an experimental “Workspace agents” toggle (off by default).
New Features
useTerminalAgentBindings(includes idle); label and icon resolved viaBUILTIN_AGENT_LABELS/usePresetIcon.StatusIndicatorwhen active, agent icon when idle; fixed icon slot to avoid layout shift; tooltip with name and status; click focuses the agent’s terminal (terminalId/focusRequestId).startedAt; summary count reflects all bound agents.Refactors
useWorkspaceAgentsRowEnabledwith an Experimental Settings switch and search entry; bindings query supports{ enabled }to skip when off.inlineWorkspacePortsEnabled.flex flex-wrapand wrap only when needed.Written for commit 6cb32c6. Summary will update on new commits.
Summary by CodeRabbit